#ifndef MEMORYCACHEMANAGER_H
#define MEMORYCACHEMANAGER_H

#include <QObject>
#include <QImage>
#include <QHash>
#include <list>
#include <QMutex>
#include <QMutexLocker>
#include <QString>
#include <optional>

// MemoryCacheManager provides in-memory L1 (composite pages) and L2 (original pages)
// caches with byte-capacity limits and a simple LRU eviction policy.
// Thread-safe via an internal mutex; designed for read-dominant workloads.
class MemoryCacheManager : public QObject {
    Q_OBJECT
public:
    explicit MemoryCacheManager(QObject* parent = nullptr);

    // Process-lifetime accessor for metrics consumers
    static MemoryCacheManager* instance();

    void setCapacityMB(int totalMB, int l1MB, int l2MB);

    // L2 Original pages
    std::optional<QImage> getOriginal(const QString& fileIdentity, int pageIndex, int dpiBucket);
    void putOriginal(const QString& fileIdentity, int pageIndex, int dpiBucket, const QImage& image);

    // L1 Composite pages
    std::optional<QImage> getComposite(const QString& fileIdentity, int mode, int compositeIndex, int dpiBucket);
    void putComposite(const QString& fileIdentity, int mode, int compositeIndex, int dpiBucket, const QImage& image);

    // Pin/unpin lifecycle helpers to prevent eviction while in-use
    void pinComposite(const QString& key);
    void unpinComposite(const QString& key);
    void pinOriginal(const QString& key);
    void unpinOriginal(const QString& key);

    // File-specific cache clearing (补完文件清理机制)
    void clearFileCache(const QString& fileIdentity);

    // Metrics (per run; not persisted)
    void resetMetrics();
    quint64 l2Hits() const;
    quint64 l2Misses() const;
    size_t totalUsedBytes() const;      // L1 + L2 used
    size_t totalCapacityBytes() const;  // L1 + L2 capacity

signals:
    void evicted(const QString& key, bool isComposite);

private:
    struct CacheEntry {
        QImage image;
        size_t bytes = 0;
        quint64 lastUsedTick = 0;
        bool pinned = false;
    };

    // Keys are pre-concatenated strings for simplicity and to avoid expensive hashing of tuples
    QString makeOriginalKey(const QString& fileIdentity, int pageIndex, int dpiBucket) const;
    QString makeCompositeKey(const QString& fileIdentity, int mode, int compositeIndex, int dpiBucket) const;

    void touchEntry(const QString& key, QHash<QString, CacheEntry>& map, std::list<QString>& lru);
    void maybeEvict();
    void evictFrom(QHash<QString, CacheEntry>& map, std::list<QString>& lru, size_t& usedBytes, size_t targetBytes);

    static size_t imageBytes(const QImage& img);

private:
    mutable QMutex mutex_;

    // L1 (composite)
    QHash<QString, CacheEntry> l1Map_;
    std::list<QString> l1Lru_;
    size_t l1CapacityBytes_ = 64ull * 1024ull * 1024ull; // default 64MB
    size_t l1UsedBytes_ = 0;

    // L2 (original)
    QHash<QString, CacheEntry> l2Map_;
    std::list<QString> l2Lru_;
    size_t l2CapacityBytes_ = 256ull * 1024ull * 1024ull; // default 256MB
    size_t l2UsedBytes_ = 0;

    // Global tracking
    quint64 tick_ = 0;

    // Metrics
    quint64 l2Hits_ = 0;
    quint64 l2Misses_ = 0;
};

#endif // MEMORYCACHEMANAGER_H


